<?php

// Copyright 2020 Catchpoint Systems Inc.
// Use of this source code is governed by the Polyform Shield 1.0.0 license that can be
// found in the LICENSE.md file.
require_once INCLUDES_PATH . '/common_lib.inc';
require_once INCLUDES_PATH . '/page_data.inc';

function GetNumeric($value)
{
    if (preg_match('/^[0-9]+$/', $value)) {
        $value = intval($value);
    } else {
        $value = floatval($value);
    }
    return $value;
}

/**
 * ChartColumn lightens a color string.
 *
 * @param string color Color string in #RRGGBB hex format.
 *
 * @return string #RRGGBB string representing lighter version of same color.
 */
function lighten($color)
{
    $red = hexdec(substr($color, 1, 2));
    $green = hexdec(substr($color, 3, 2));
    $blue = hexdec(substr($color, 5, 2));
    $new_red = floor($red / 2 + 128);
    $new_green = floor($green / 2 + 128);
    $new_blue = floor($blue / 2 + 128);
    return ("#" . dechex($new_red) . dechex($new_green) . dechex($new_blue));
}

/**
 * ChartColumn is a class describing a column of a GViz chart.
 */
class ChartColumn
{
    public $values; // @var float[] Values in column
    public $color; // @var string Color in chart
    public $line; // @var bool: true for line, false for point
    public $label; // @var string: Label of column in chart

    function __construct($values, $color, $line, $label)
    {
        $this->values = $values;
        $this->color = $color;
        $this->line = $line;
        $this->label = $label;
    }

  /**
   * fromPageData returns a ChartColumn with points in the given color,
   * corresponding to all values from a given cached status and metric
   * of a given pageData object.
   *
   * @param (int|float)[][][] &$pageData
   * @param int cached
   * @param string metric
   * @param string color
   * @param string label
   *
   * @return ChartColumn
   */
    public static function fromPageData(&$pageData, $cached, $metric, $color, $label)
    {
      // For each run in pageData, pull out the data values, then can create the instance
        $values = values($pageData, $cached, $metric, true);
        return new ChartColumn($values, $color, false, $label);
    }

  /**
   * medianFromPageData returns a ChartColumn with a line in the given color,
   * corresponding to the median of values from a given cached status and metric
   * of a given pageData object.
   *
   * @param (int|float)[][][] &$pageData
   * @param int cached
   * @param string metric
   * @param string color
   * @param string label
   *
   * @return ChartColumn
   */
    public static function medianFromPageData($pageData, $cached, $metric, $color, $label, $num_runs)
    {
      // For each run in pageData, pull out the data values and compute the median, then can create the instance
        $values = values($pageData, $cached, $metric, true);
        $value = median($values);
        if (!$value) {
            return null;
        }
        return new ChartColumn(array_combine(range(1, $num_runs), array_fill(1, $num_runs, $value)), $color, true, $label);
    }

  /**
   * medianFromPageData returns a ChartColumn with a line in the given color,
   * corresponding to the value of $metric on the run whose value
   * of $median_metric is median, drawn from all runs from the given cached
   * status in the given pageData object.
   *
   * @param (int|float)[][][] &$pageData
   * @param int cached
   * @param string metric
   * @param string color
   * @param string label
   *
   * @return ChartColumn
   */
    public static function medianRunFromPageData($pageData, $cached, $metric, $median_metric, $color, $label, $num_runs)
    {
        $medianRun = $pageData[GetMedianRun($pageData, $cached, $median_metric)][$cached];
        if (!array_key_exists($metric, $medianRun)) {
            return null;
        }
        $value = GetNumeric($medianRun[$metric]);
        return new ChartColumn(array_combine(range(1, $num_runs), array_fill(1, $num_runs, $value)), $color, true, $label);
    }

  /**
   * runs returns a ChartColumn with the sequence of integers from 1 to $runs.
   *
   * @param int runs
   *
   * @return ChartColumn
   */
    public static function runs($runs)
    {
        return new ChartColumn(array_combine(range(1, $runs), range(1, $runs)), null, null, 'run');
    }

  /**
   * dataMedianColumns returns a ChartColumn[] with three columns: data points
   * of $metric, line corresponding to median of $metric, and line
   * corresponding to value of $metric on run whose value of $median_metric
   * is median.
   */
    public static function dataMedianColumns($pageData, $cached, $metric, $median_metric, $color, $color_median_run, $labels, $num_runs, $show_median_run, $show_median_value)
    {
        $chartColumns = array();
        $label = implode(" ", $labels);
        $chartColumns[] = ChartColumn::fromPageData($pageData, $cached, $metric, $color, $label);
        if ($show_median_run) {
            $label_median_run = implode(" ", array_merge($labels, (array)('Run with Median ' . $median_metric)));
            $column = ChartColumn::medianRunFromPageData($pageData, $cached, $metric, $median_metric, $color_median_run, $label_median_run, $num_runs);
            if (!is_null($column)) {
                $chartColumns[] = $column;
            }
        }
        if ($show_median_value) {
            $label_median_value = implode(" ", array_merge($labels, (array)('Median')));
            $column = ChartColumn::medianFromPageData($pageData, $cached, $metric, $color, $label_median_value, $num_runs);
            if (!is_null($column)) {
                $chartColumns[] = $column;
            }
        }
        return $chartColumns;
    }
}

/**
 * CompareFrom is a class describing a statistical comparison against a control.
 */
class CompareFrom
{
    public $confData; // @var ConfData Mean and confidence interval.
    public $diff; // @var float Difference from control, or NULL for the control.
    public $pValue; // @var float P-Value as compared against control, or NULL
                  // for the control.

    function __construct($confData, $diff, $pValue)
    {
        $this->confData = $confData;
        $this->diff = $diff;
        $this->pValue = $pValue;
    }
}

/**
 * CompareTable is a class describing a statistical comparison table.
 */
class CompareTable
{
    public $div; // @var string name of HTML div element
    public $compareFrom; // @var CompareFrom[]

    function __construct($div, $compareFrom)
    {
        $this->div = $div;
        $this->compareFrom = $compareFrom;
    }
}

/**
 * Chart is a class describing a GViz chart.
 */
class Chart
{
    public $div; // @var string name of HTML div element
    public $columns; // @var ChartColumn[] Columns in chart
    public $compareData; // @var CompareTable Comparison data table

    function __construct($div, $columns, $compareData)
    {
        $this->div = $div;
        $this->columns = $columns;
        $this->compareData = $compareData;
    }
}
